package org.helgoboss.scala_additions

import collection.mutable.{HashSet, Set}
import PathPattern._
import FileTreeIterator.Node

trait PatternCriteria {
    private var includePatterns = new HashSet[PathPattern]
    private var excludePatterns = new HashSet[PathPattern]
    
    protected abstract class Completeness
    protected case object Neither extends Completeness
    protected case object OnlyIncludes extends Completeness
    protected case object OnlyExcludes extends Completeness
    protected case object Both extends Completeness
    
    protected lazy val completeness = {
        if (includePatterns.isEmpty && excludePatterns.isEmpty) Neither
        else if (includePatterns.isEmpty && !excludePatterns.isEmpty) OnlyExcludes
        else if (!includePatterns.isEmpty && excludePatterns.isEmpty) OnlyIncludes
        else Both
    }
    
    def ~:(node: Node) = isMatchedBy(node)
    
    def isMatchedBy(node: Node) = {
        lazy val includePatternsMatchResult = includePatterns.map(_ matchAgainst node.path)
        lazy val excludePatternsMatchResult = excludePatterns.map(_ matchAgainst node.path)
        
        lazy val nodeMatchesIncludePatterns = includePatternsMatchResult.exists(_ == Matches)
        lazy val nodeMatchesExcludePatterns = excludePatternsMatchResult.exists(_ == Matches)
        
        lazy val subNodeCanMatchIncludePatterns = node.file.isDirectory && includePatternsMatchResult.exists(_ != NeverMatches)
        lazy val subNodeDefinitelyMatchesExcludePatterns = node.file.isDirectory && excludePatternsMatchResult.exists(_ == WillMatchEverySubPath)
        
        completeness match {
            case Neither => 
                node.recurse = true
                true
                
            case OnlyIncludes =>
                node.recurse = subNodeCanMatchIncludePatterns
                nodeMatchesIncludePatterns
            
            case OnlyExcludes =>
                node.recurse = !subNodeDefinitelyMatchesExcludePatterns
                !nodeMatchesExcludePatterns
                
            case Both => 
                node.recurse = subNodeCanMatchIncludePatterns && !subNodeDefinitelyMatchesExcludePatterns
                nodeMatchesIncludePatterns && !nodeMatchesExcludePatterns
        }
    }

    protected def include(patternExpressions: String*) {
        addToPatternSet(includePatterns, patternExpressions)
    }
    
    protected def exclude(patternExpressions: String*) {
        addToPatternSet(excludePatterns, patternExpressions)
    }
    
    
    private def addToPatternSet(set: Set[PathPattern], patternExpressions: Collection[String]) {
        patternExpressions foreach { e =>
            set += PathPattern(e)
        }
    }

}
